<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>Electric strings</title>
  <script>
window.requestAnimFrame = (function() {
  return (
    window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function(callback) {
      window.setTimeout(callback);
    }
  );
});

function init(elemid) {
  let canvas = document.getElementById(elemid),
    c = canvas.getContext("2d"),
    w = (canvas.width = window.innerWidth),
    h = (canvas.height = window.innerHeight);
  c.fillStyle = "rgba(30,30,30,1)";
  c.fillRect(0, 0, w, h);
  return {c:c,canvas:canvas};
}
</script>

</head>
<body style="background: rgb(30,30,30);">
<!-- partial:index.partial.html -->
<canvas id="canvas"></canvas>
<!-- partial -->
  <script>
    window.onload = function() {
      let c = init("canvas").c,
        canvas = init("canvas").canvas,
        w = (canvas.width = window.innerWidth),
        h = (canvas.height = window.innerHeight),
        mouse = { x: false, y: false },
        last_mouse = {};

      //initiation

      function dist(p1x, p1y, p2x, p2y) {
        return Math.sqrt(Math.pow(p2x - p1x, 2) + Math.pow(p2y - p1y, 2));
      }

      class segment {
        constructor(parent, l, a, first) {
          this.first = first;
          if (first) {
            this.pos = {
              x: parent.x,
              y: parent.y
            };
          } else {
            this.pos = {
              x: parent.nextPos.x,
              y: parent.nextPos.y
            };
          }
          this.l = l;
          this.ang = a;
          this.nextPos = {
            x: this.pos.x + this.l * Math.cos(this.ang),
            y: this.pos.y + this.l * Math.sin(this.ang)
          };
        }
        update(t) {
          this.ang = Math.atan2(t.y - this.pos.y, t.x - this.pos.x);
          this.pos.x = t.x + this.l * Math.cos(this.ang - Math.PI);
          this.pos.y = t.y + this.l * Math.sin(this.ang - Math.PI);
          this.nextPos.x = this.pos.x + this.l * Math.cos(this.ang);
          this.nextPos.y = this.pos.y + this.l * Math.sin(this.ang);
        }
        fallback(t) {
          this.pos.x = t.x;
          this.pos.y = t.y;
          this.nextPos.x = this.pos.x + this.l * Math.cos(this.ang);
          this.nextPos.y = this.pos.y + this.l * Math.sin(this.ang);
        }
        show() {
          c.lineTo(this.nextPos.x, this.nextPos.y);
        }
      }

      class tentacle {
        constructor(x, y, l, n, a) {
          this.x = x;
          this.y = y;
          this.l = l;
          this.n = n;
          this.t = {};
          this.rand = Math.random();
          this.segments = [new segment(this, this.l / this.n, 0, true)];
          for (let i = 1; i < this.n; i++) {
            this.segments.push(
              new segment(this.segments[i - 1], this.l / this.n, 0, false)
            );
          }
        }
        move(last_target, target) {
          this.angle = Math.atan2(target.y-this.y,target.x-this.x);
          this.dt = dist(last_target.x, last_target.y, target.x, target.y)+5;
          this.t = {
            x: target.x - 0.8*this.dt*Math.cos(this.angle),
            y: target.y - 0.8*this.dt*Math.sin(this.angle)
          };
          if(this.t.x){
            this.segments[this.n - 1].update(this.t);
          }else{
            this.segments[this.n - 1].update(target);
          }
          for (let i = this.n - 2; i >= 0; i--) {
            this.segments[i].update(this.segments[i + 1].pos);
          }
          if (
            dist(this.x, this.y, target.x, target.y) <=
            this.l + dist(last_target.x, last_target.y, target.x, target.y)
          ) {
            this.segments[0].fallback({ x: this.x, y: this.y });
            for (let i = 1; i < this.n; i++) {
              this.segments[i].fallback(this.segments[i - 1].nextPos);
            }
          }
        }
        show(target) {
          if (dist(this.x, this.y, target.x, target.y) <= this.l) {
            c.globalCompositeOperation = "color-dodge";
            c.beginPath();
            c.lineTo(this.x, this.y);
            for (let i = 0; i < this.n; i++) {
              this.segments[i].show();
            }
            c.strokeStyle = "hsl("+(this.rand*60+180)+",100%," + (this.rand * 60 + 25) + "%)";
            c.lineWidth = this.rand * 2;
            c.lineCap="round";
            c.lineJoin="round";
            c.stroke();
            c.globalCompositeOperation = "source-over";
          }
        }
        show2(target) {
          c.beginPath();
          if (dist(this.x, this.y, target.x, target.y) <= this.l) {
            c.arc(this.x, this.y, 2*this.rand+1, 0, 2 * Math.PI);
            c.fillStyle = "white";
          } else {
            c.arc(this.x, this.y, this.rand*2, 0, 2 * Math.PI);
            c.fillStyle = "darkcyan";
          }
          c.fill();
        }
      }

      let maxl = 300,
        minl = 50,
        n = 30,
        numt = 500,
        tent = [],
        clicked = false,
        target = { x: 0, y: 0 },
        last_target = {},
        t = 0,
        q = 10;

      for (let i = 0; i < numt; i++) {
        tent.push(
          new tentacle(
            Math.random() * w,
            Math.random() * h,
            Math.random() * (maxl - minl) + minl,
            n,
            Math.random() * 2 * Math.PI
          )
        );
      }
      function draw() {
        //animation
        if (mouse.x) {
          target.errx = mouse.x - target.x;
          target.erry = mouse.y - target.y;
        } else {
          target.errx =
            w / 2 +
            (h / 2 - q) *
              Math.sqrt(2) *
              Math.cos(t) /
              (Math.pow(Math.sin(t), 2) + 1) -
            target.x;
          target.erry =
            h / 2 +
            (h / 2 - q) *
              Math.sqrt(2) *
              Math.cos(t) *
              Math.sin(t) /
              (Math.pow(Math.sin(t), 2) + 1) -
            target.y;
        }

        target.x += target.errx / 10;
        target.y += target.erry / 10;

        t += 0.01;
        
        c.beginPath();
        c.arc(target.x, target.y, dist(last_target.x, last_target.y, target.x, target.y)+5, 0, 2 * Math.PI);
        c.fillStyle = "hsl(210,100%,80%)";
        c.fill();

        for (i = 0; i < numt; i++) {
          tent[i].move(last_target, target);
          tent[i].show2(target);
        }
        for (i = 0; i < numt; i++) {
          tent[i].show(target);
        }
        last_target.x = target.x;
        last_target.y = target.y;
      }

      canvas.addEventListener(
        "mousemove",
        function(e) {
          last_mouse.x = mouse.x;
          last_mouse.y = mouse.y;

          mouse.x = e.pageX - this.offsetLeft;
          mouse.y = e.pageY - this.offsetTop;
        },
        false
      );

      canvas.addEventListener("mouseleave", function(e) {
        mouse.x = false;
        mouse.y = false;
      });

      canvas.addEventListener(
        "mousedown",
        function(e) {
          clicked = true;
        },
        false
      );

      canvas.addEventListener(
        "mouseup",
        function(e) {
          clicked = false;
        },
        false
      );

      function loop() {
        window.requestAnimFrame(loop);
        // c.fillStyle="rgba(30,30,30,0.1)";
        // c.fillRect(0, 0, w, h);
        c.clearRect(0, 0, w, h);
        draw();
      }

      window.addEventListener("resize", function() {
        (w = canvas.width = window.innerWidth),
          (h = canvas.height = window.innerHeight);
        loop();
      });

      loop();
      setInterval(loop, 1000 / 60);
    };
  </script>

</body>
</html>